"use strict";
/**
 * @file Hive asset type definitions and helpers.
 * @author Johan Nordberg <code@johan-nordberg.com>
 * @license
 * Copyright (c) 2017 Johan Nordberg. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *  1. Redistribution of source code must retain the above copyright notice, this
 *     list of conditions and the following disclaimer.
 *
 *  2. Redistribution in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *
 *  3. Neither the name of the copyright holder nor the names of its contributors
 *     may be used to endorse or promote products derived from this software without
 *     specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * You acknowledge that this software is not designed, licensed or intended for use
 * in the design, construction, operation or maintenance of any military facility.
 */
Object.defineProperty(exports, "__esModule", { value: true });
const assert = require("assert");
/**
 * Class representing a hive asset, e.g. `1.000 HIVE` or `12.112233 VESTS`.
 */
class Asset {
    constructor(amount, symbol) {
        this.amount = amount;
        this.symbol = symbol;
    }
    /**
     * Create a new Asset instance from a string, e.g. `42.000 HIVE`.
     */
    static fromString(string, expectedSymbol) {
        const [amountString, symbol] = string.split(' ');
        if (!['HIVE', 'VESTS', 'HBD', 'TESTS', 'TBD', 'SBD', 'STEEM'].includes(symbol)) {
            throw new Error(`Invalid asset symbol: ${symbol}`);
        }
        if (expectedSymbol && symbol !== expectedSymbol) {
            throw new Error(`Invalid asset, expected symbol: ${expectedSymbol} got: ${symbol}`);
        }
        const amount = Number.parseFloat(amountString);
        if (!Number.isFinite(amount)) {
            throw new Error(`Invalid asset amount: ${amountString}`);
        }
        return new Asset(amount, symbol);
    }
    /**
     * Convenience to create new Asset.
     * @param symbol Symbol to use when created from number. Will also be used to validate
     *               the asset, throws if the passed value has a different symbol than this.
     */
    static from(value, symbol) {
        if (value instanceof Asset) {
            if (symbol && value.symbol !== symbol) {
                throw new Error(`Invalid asset, expected symbol: ${symbol} got: ${value.symbol}`);
            }
            return value;
        }
        else if (typeof value === 'number' && Number.isFinite(value)) {
            return new Asset(value, symbol || 'STEEM');
        }
        else if (typeof value === 'string') {
            return Asset.fromString(value, symbol);
        }
        else {
            throw new Error(`Invalid asset '${String(value)}'`);
        }
    }
    /**
     * Return the smaller of the two assets.
     */
    static min(a, b) {
        assert(a.symbol === b.symbol, 'can not compare assets with different symbols');
        return a.amount < b.amount ? a : b;
    }
    /**
     * Return the larger of the two assets.
     */
    static max(a, b) {
        assert(a.symbol === b.symbol, 'can not compare assets with different symbols');
        return a.amount > b.amount ? a : b;
    }
    /**
     * Return asset precision.
     */
    getPrecision() {
        switch (this.symbol) {
            case 'TESTS':
            case 'TBD':
            case 'HIVE':
            case 'HBD':
            case 'SBD':
            case 'STEEM':
                return 3;
            case 'VESTS':
                return 6;
        }
    }
    /**
     * returns a representation of this asset using only STEEM SBD for
     * legacy purposes
     */
    steem_symbols() {
        switch (this.symbol) {
            case 'HIVE':
                return Asset.from(this.amount, 'STEEM');
            case 'HBD':
                return Asset.from(this.amount, 'SBD');
            default:
                return this;
        }
    }
    /**
     * Return a string representation of this asset, e.g. `42.000 HIVE`.
     */
    toString() {
        return `${this.amount.toFixed(this.getPrecision())} ${this.symbol}`;
    }
    /**
     * Return a new Asset instance with amount added.
     */
    add(amount) {
        const other = Asset.from(amount, this.symbol);
        assert(this.symbol === other.symbol, 'can not add with different symbols');
        return new Asset(this.amount + other.amount, this.symbol);
    }
    /**
     * Return a new Asset instance with amount subtracted.
     */
    subtract(amount) {
        const other = Asset.from(amount, this.symbol);
        assert(this.symbol === other.symbol, 'can not subtract with different symbols');
        return new Asset(this.amount - other.amount, this.symbol);
    }
    /**
     * Return a new Asset with the amount multiplied by factor.
     */
    multiply(factor) {
        const other = Asset.from(factor, this.symbol);
        assert(this.symbol === other.symbol, 'can not multiply with different symbols');
        return new Asset(this.amount * other.amount, this.symbol);
    }
    /**
     * Return a new Asset with the amount divided.
     */
    divide(divisor) {
        const other = Asset.from(divisor, this.symbol);
        assert(this.symbol === other.symbol, 'can not divide with different symbols');
        return new Asset(this.amount / other.amount, this.symbol);
    }
    /**
     * For JSON serialization, same as toString().
     */
    toJSON() {
        return this.toString();
    }
}
exports.Asset = Asset;
/**
 * Represents quotation of the relative value of asset against another asset.
 * Similar to 'currency pair' used to determine value of currencies.
 *
 *  For example:
 *  1 EUR / 1.25 USD where:
 *  1 EUR is an asset specified as a base
 *  1.25 USD us an asset specified as a qute
 *
 *  can determine value of EUR against USD.
 */
class Price {
    /**
     * @param base  - represents a value of the price object to be expressed relatively to quote
     *                asset. Cannot have amount == 0 if you want to build valid price.
     * @param quote - represents an relative asset. Cannot have amount == 0, otherwise
     *                asertion fail.
     *
     * Both base and quote shall have different symbol defined.
     */
    constructor(base, quote) {
        this.base = base;
        this.quote = quote;
        assert(base.amount !== 0 && quote.amount !== 0, 'base and quote assets must be non-zero');
        assert(base.symbol !== quote.symbol, 'base and quote can not have the same symbol');
    }
    /**
     * Convenience to create new Price.
     */
    static from(value) {
        if (value instanceof Price) {
            return value;
        }
        else {
            return new Price(Asset.from(value.base), Asset.from(value.quote));
        }
    }
    /**
     * Return a string representation of this price pair.
     */
    toString() {
        return `${this.base}:${this.quote}`;
    }
    /**
     * Return a new Asset with the price converted between the symbols in the pair.
     * Throws if passed asset symbol is not base or quote.
     */
    convert(asset) {
        if (asset.symbol === this.base.symbol) {
            assert(this.base.amount > 0);
            return new Asset((asset.amount * this.quote.amount) / this.base.amount, this.quote.symbol);
        }
        else if (asset.symbol === this.quote.symbol) {
            assert(this.quote.amount > 0);
            return new Asset((asset.amount * this.base.amount) / this.quote.amount, this.base.symbol);
        }
        else {
            throw new Error(`Can not convert ${asset} with ${this}`);
        }
    }
}
exports.Price = Price;
